﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Nini.Config;
using System.Drawing.Drawing2D;
using System.Xml;
using GDIDB;
using System.Windows.Forms;

namespace PowerControl
{
    public class Simulation
    {

        private BaseStation baseStation;
        private List<MobileDevice> mobileDevices;
        private PropagationModel propagationModel;
        private Image mobileIcon;
        private Image baseIcon;

        private double workingFrequency;

        private IntervalTimer timer;

        private double tpcInterval;

        private int tpcWindowSize;

        public int TpcWindowSize
        {
            get { return tpcWindowSize; }
            set { tpcWindowSize = value; }
        }

        public double TpcInterval
        {
            get { return tpcInterval; }
            set { tpcInterval = value; }
        }

        public double WorkingFrequency
        {
            get { return workingFrequency; }
            set { workingFrequency = value; }
        }

        private double minX;
        public double MinX
        {
            get { return minX; }
            set { minX = value; }
        }
        private double maxX;

        public double MaxX
        {
            get { return maxX; }
            set { maxX = value; }
        }
        private double minY;

        public double MinY
        {
            get { return minY; }
            set { minY = value; }
        }
        private double maxY;

        public double MaxY
        {
            get { return maxY; }
            set { maxY = value; }
        }

        public Simulation()
        {
            //Initialize objects
            this.baseStation = new BaseStation();
            this.mobileDevices = new List<MobileDevice>();

            //Load UI icons
            mobileIcon = Image.FromFile(Application.StartupPath+"\\res\\mob.png");
            baseIcon = Image.FromFile(Application.StartupPath + "\\res\\base.png");
            //

            //Set the default propagation model
            this.propagationModel = new FriisPropagationModel();

            //Load config files and stuff...
            //Load Mobile services information
                ServicesManager.Instance.Init(@"config\services.ini");
            //

                timer = new IntervalTimer();
        }

        public BaseStation BaseStation
        {
            get
            {
                return this.baseStation;
            }
            set
            {
                this.baseStation = value;
            }
        }

        public List<MobileDevice> MobileDevices
        {
            get
            {
                return this.mobileDevices;
            }
            set
            {
                this.mobileDevices = value;
            }
        }

        public PropagationModel PropagationModel
        {
            get
            {
                return propagationModel;
            }
            set
            {
                this.propagationModel = value;
            }
        }

        private double getDownlinkReceivedPower(MobileDevice d)
        {
            return this.PropagationModel.getReceivedPower(this.BaseStation.Antenna, d.Antenna, this.workingFrequency);
        }

        private double getUplinkEmittedPower(MobileDevice d)
        {
            double signal = this.PropagationModel.getReceivedPower(d.Antenna, this.BaseStation.Antenna, this.workingFrequency);
            return signal;
        }

        private double getUplinkInterference(MobileDevice d)
        {
            double res = 0.0;
            for (int i = 0; i < this.MobileDevices.Count; ++i)
            {
                if (this.MobileDevices[i] != d)
                {
                    res += this.PropagationModel.getReceivedPower(this.MobileDevices[i].Antenna, this.BaseStation.Antenna, this.workingFrequency);
                }
            }

            //Thermal noise for bandwidth of 3.84MHz at 20 deg C
            double th_noise = Utils.ThermalNoise(3840000, 20);
            res += th_noise;
            return res;
        }

        public void applyPowerControl(MobileDevice m)
        {            
            Service s = m.ServiceType;
            double CIRest = Utils.wattTodB(m.UplinkEmittedPower / m.UplinkInterference);
            double CIRtar = s.CIRTargetUplink + 1.0;

            //Inner Loop
            if (CIRtar > CIRest)
            {
                    m.OrderTPC(1);
                    return;
            }
            else if( CIRtar < CIRest )
            {
                    m.OrderTPC(-1);
                    return;
            }
            //
        }

        public void Step(double deltaTime)
        {
            double delta;

            if( timer.State == IntervalTimer.TimerState.Started )
            {
                timer.Stop();
                delta = timer.GetSeconds();
            }
            else
                delta = deltaTime;
            timer.Stop();

            int iter = (int)(delta / 0.000666666667);

            for (int k = 0; k < iter; ++k)
            {
                //Update Mobile Devices Received Power
                for (int i = 0; i < this.MobileDevices.Count; ++i)
                {
                    MobileDevice m = this.MobileDevices[i];
                    m.DownlinkReceivedPower = getDownlinkReceivedPower(m);
                    m.UplinkEmittedPower = getUplinkEmittedPower(m);
                    m.UplinkInterference = getUplinkInterference(m);
                    m.Step(0.000666666667);

                    //Check if the mobile go out of the screen
                    m.Position.X = Math.Min(MaxX, m.Position.X);
                    m.Position.X = Math.Max(MinX, m.Position.X);
                    m.Position.Y = Math.Min(MaxY, m.Position.Y);
                    m.Position.Y = Math.Max(MinY, m.Position.Y);

                    //Closed Loop PC
                    applyPowerControl(m);
                }

                for (int i = 0; i < this.MobileDevices.Count; ++i)
                    MobileDevices[i].ApplyTPC();
            }
            
            timer.Start();
        }

        public void DrawRadiationPattern(Graphics g, int pWidth, int pHeight, List<Antenna> l, double Frequency, double a, double b, int cellSize, Color col)
        {
            PropagationModel model = new FriisPropagationModel();
            Antenna probe = new MobileAntenna();
            probe.Gain = 0;

            g.Clear(Color.Black);

            int midX = pWidth / 2;
            int midY = pHeight / 2;
            int width = cellSize;
            for (int x = -midX; x <= midX; x+=width)
            {
                for (int y = -midY; y <= midY; y+=width)
                {
                    probe.Position.X = x;
                    probe.Position.Y = y;
                    double val = 0.0;
                    for (int i = 0; i < l.Count; ++i)
                    {
                        val += model.getReceivedPower(l[i], probe, this.workingFrequency);
                    }
                    val = Utils.wattTodB(val);
                    double k = (val - a) / (b - a);

                    int shader = Math.Max((int)(Math.Min((k*(double)(col.R)),255.0)),0);
                    int shadeg = Math.Max((int)(Math.Min((k * (double)(col.G)), 255.0)), 0);
                    int shadeb = Math.Max((int)(Math.Min((k * (double)(col.B)), 255.0)), 0);

                    Brush br = new SolidBrush(Color.FromArgb(shader,shadeg,shadeb));
                   
                    g.FillRectangle(br, midX + (x - width/2), midY + (y - width/2), width, width); 
                }
            }
        }

        //Draw the simulation in a PictureBox
        public void DrawSimulation(Graphics g, int pWidth, int pHeight)
        {
            g.Clear(Color.White);
            Pen pn = new Pen(Color.Blue);
            Pen pn2 = new Pen(Color.Red);
            Font fnt = new Font("Tahoma", 10);

            //If there is a simulation running...
            {
                int midX = pWidth / 2;
                int midY = pHeight / 2;

                //Draw the BS
                //g.DrawRectangle(pn2, (float)(midX), (float)(midY), 10, 10);
                g.DrawImage(baseIcon, (float)midX, (float)midY, 50, 50);
                //

                //Draw the mobiles
                for (int i = 0; i < this.MobileDevices.Count; ++i)
                {
                    MobileDevice m = this.MobileDevices[i];
                    //g.DrawRectangle(pn, (float)midX + (float)(m.Antenna.Position.X), (float)midY + (float)m.Antenna.Position.Y, 10, 10);
                    g.DrawImage(mobileIcon, (float)midX + (float)(m.Antenna.Position.X), (float)midY + (float)m.Antenna.Position.Y, 40, 40);
                    g.DrawString(m.ID.ToString(), fnt, Brushes.Black,(float)midX + (float)(m.Antenna.Position.X), (float)midY + ((float)m.Antenna.Position.Y)- 10.0f); 
                
                    //Draw Arrows representing mobile directions
                    g.DrawLine(pn, (float)midX+(float)(m.Position.X)+25.0f, (float)midY+(float)(m.Position.Y)+25.0f, (float)midX+(float)(m.Position.X + Math.Cos(m.Position.Yaw)*50.0f)+25.0f, (float)midY+(float)(m.Position.Y + Math.Sin(m.Position.Yaw)*50.0f)+25.0f);        
                }
            }
        }

        static public Simulation FromXML(String Filename)
        {
            Simulation sim = new Simulation();
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(Filename);
            XmlNodeList slist = xDoc.GetElementsByTagName("simulation");
            //Get the first "simulation" stuff
            XmlNode root = slist.Item(0);
            
            //Process the inner nodes
            XmlNodeList inlist = root.ChildNodes;
            for (int i = 0; i < inlist.Count; ++i)
            {
                XmlNode n = inlist.Item(i);
                if (n.Name == "mobile")
                {
                    //It's A mobile node
                    sim.MobileDevices.Add(MobileDevice.FromXMLNode(n));
                }

                //It's THE base station
                if (n.Name == "basestation")
                {
                    sim.BaseStation = BaseStation.FromXMLNode(n);
                }

                //Working Frequency
                if (n.Name == "working_frequency")
                {
                    double freq = Double.Parse(n.InnerText);
                    sim.workingFrequency = freq;
                }
            }
            //
            return sim;
        }
    }
}
